package com.lingz.manager.config.security.jwt;

import com.lingz.manager.common.annotation.SystemLog;
import com.lingz.manager.common.constant.SecurityConstant;
import com.lingz.manager.common.enums.LogType;
import com.lingz.manager.common.utils.ResponseUtil;
import com.lingz.manager.common.vo.TokenUser;
import cn.hutool.core.util.StrUtil;
import com.google.gson.Gson;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 登录成功处理类
 * @author fanyoucai
 */
@Slf4j
@Component
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Value("${xboot.token.redis}")
    private Boolean tokenRedis;

    @Value("${xboot.tokenExpireTime}")
    private Integer tokenExpireTime;

    @Value("${xboot.saveLoginTime}")
    private Integer saveLoginTime;

    /**
     * 密钥
     */
    @Value("${DEFAULT_KEY}")
    private String DEFAULT_KEY;

    /**
     * 文件名
     */
    @Value("${FILE_NAME}")
    private  String FILE_NAME;

    @Override
    @SystemLog(description = "登录系统", type = LogType.LOGIN)
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        String path = System.getProperty("user.dir");
        path=path+"/"+FILE_NAME;
        System.out.println(path);
        File file = new File(path);
        FileReader reader=null;
        BufferedReader br=null;
        try {
            if(file.exists()){
                reader = new FileReader(path);
                br = new BufferedReader(reader);
                StringBuilder builder = new StringBuilder();
                String s;
                while((s=br.readLine())!=null){
                    builder.append(s);
                }
                if(StringUtils.isEmpty(builder.toString())){
                    ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"文件信息为空"));

                }
                String str = builder.toString();
                System.out.println(str);
                String decodeStr = decrypt_base64(str, DEFAULT_KEY);
                System.out.println(decodeStr);
                if(!decodeStr.contains("||")){
                    ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"解密数据存在异常"));
                }
                String[] split = decodeStr.split("\\|\\|");
                System.out.println(split.length);

                if(split.length!=3) ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"解密数据有误"));
                List<String> macList = getMacList();
                if(macList.size()<1) ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"获取本地的mac失败"));
                System.out.println(split[0]);
                if (true) {
                    SimpleDateFormat f = new SimpleDateFormat("yyyyMMddHHmmss");
                    long i1 = f.parse(split[1]).getTime();
                    long i2 = new Date().getTime();
                    if(i1>i2){
                        //用户选择保存登录状态几天
                        String saveLogin = request.getParameter(SecurityConstant.SAVE_LOGIN);
                        Boolean saved = false;
                        if(StrUtil.isNotBlank(saveLogin) && Boolean.valueOf(saveLogin)){
                            saved = true;
                            if(!tokenRedis){
                                tokenExpireTime = saveLoginTime * 60 * 24;
                            }
                        }
                        String username = ((UserDetails)authentication.getPrincipal()).getUsername();
                        List<GrantedAuthority> authorities = (List<GrantedAuthority>) ((UserDetails)authentication.getPrincipal()).getAuthorities();
                        List<String> list = new ArrayList<>();
                        for(GrantedAuthority g : authorities){
                            list.add(g.getAuthority());
                        }
                        // 登陆成功生成token
                        String token;
                        if(tokenRedis){
                            // redis
                            token = UUID.randomUUID().toString().replace("-", "");
                            TokenUser user = new TokenUser(username, list, saved);
                           /* // 单点登录 之前的token失效
                            String oldToken = redisTemplate.opsForValue().get(SecurityConstant.USER_TOKEN + username);
                            if(StrUtil.isNotBlank(oldToken)){
                                redisTemplate.delete(SecurityConstant.TOKEN_PRE + oldToken);
                            }
                            if(saved){
                                redisTemplate.opsForValue().set(SecurityConstant.USER_TOKEN + username, token, saveLoginTime, TimeUnit.DAYS);
                                redisTemplate.opsForValue().set(SecurityConstant.TOKEN_PRE + token, new Gson().toJson(user), saveLoginTime, TimeUnit.DAYS);
                            }else{
                                redisTemplate.opsForValue().set(SecurityConstant.USER_TOKEN + username, token, tokenExpireTime, TimeUnit.MINUTES);
                                redisTemplate.opsForValue().set(SecurityConstant.TOKEN_PRE + token, new Gson().toJson(user), tokenExpireTime, TimeUnit.MINUTES);
                            }*/
                        }else{
                            // jwt
                            token = SecurityConstant.TOKEN_SPLIT + Jwts.builder()
                                    //主题 放入用户名
                                    .setSubject(username)
                                    //自定义属性 放入用户拥有请求权限
                                    .claim(SecurityConstant.AUTHORITIES, new Gson().toJson(list))
                                    //失效时间
                                    .setExpiration(new Date(System.currentTimeMillis() + tokenExpireTime * 60 * 1000))
                                    //签名算法和密钥
                                    .signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN_KEY)
                                    .compact();
                        }
                        ResponseUtil.out(response, ResponseUtil.resultMap(true,200,username, token));
                    }else{
                        long l = (i2 - i1)/1000/60/60/24;
                        ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"您的使用日期已过期"+l+"天,请联系管理员"));
                    }
                } else {
                    ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"mac地址不一致"));
                }
            }else{
                ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"找不到指定的文件"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(reader!=null) {
                    reader.close();
                }
                if(br!=null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        ResponseUtil.out(response, ResponseUtil.resultMap(false,200,"系统内部错误"));

    }


    /**
     * 获取mac的地址
     */
    /***因为一台机器不一定只有一个网卡呀，所以返回的是数组是很合理的***/
    public List<String> getMacList() throws Exception {
        java.util.Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
        StringBuilder sb = new StringBuilder();
        ArrayList<String> tmpMacList=new ArrayList<>();
        while(en.hasMoreElements()){
            NetworkInterface iface = en.nextElement();
            List<InterfaceAddress> addrs = iface.getInterfaceAddresses();
            for(InterfaceAddress addr : addrs) {
                InetAddress ip = addr.getAddress();
                NetworkInterface network = NetworkInterface.getByInetAddress(ip);
                if(network==null){continue;}
                byte[] mac = network.getHardwareAddress();
                if(mac==null){continue;}
                sb.delete( 0, sb.length() );
                for (int i = 0; i < mac.length; i++) {sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));}
                tmpMacList.add(sb.toString());
            }        }
        if(tmpMacList.size()<=0){return tmpMacList;}
        /***去重，别忘了同一个网卡的ipv4,ipv6得到的mac都是一样的，肯定有重复，下面这段代码是。。流式处理***/
        List<String> unique = tmpMacList.stream().distinct().collect(Collectors.toList());
        return unique;
    }




    /**
     * 加密
     *
     * @param message
     * @param key
     * @return
     * @throws Exception
     */
    public  static byte[] encrypt(String message, String key) throws Exception {
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");

        DESKeySpec desKeySpec = new DESKeySpec(key.getBytes("UTF-8"));

        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
        IvParameterSpec iv = new IvParameterSpec(key.getBytes("UTF-8"));
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);

        return cipher.doFinal(message.getBytes("UTF-8"));
    }

    /**
     * 加密，方式为先DES加密， 再base64转码的字符串
     * @param message
     * @param key
     * @return
     * @throws Exception
     */
    public static   String encrypt_base64(String message, String key) throws Exception {
        String a = toHexString(encrypt(message, key)).toUpperCase();
        String base64message =  new String(Base64.encodeBase64(a.getBytes("UTF-8")));
        return base64message;
    }

    /**
     * 解密
     *
     * @param message
     *            加密后的内容
     * @param key
     *            密钥
     * @return
     * @throws Exception
     */
    public  static String decrypt(String message, String key) throws Exception {

        byte[] bytesrc = convertHexString(message);
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        DESKeySpec desKeySpec = new DESKeySpec(key.getBytes("UTF-8"));
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
        IvParameterSpec iv = new IvParameterSpec(key.getBytes("UTF-8"));

        cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);

        byte[] retByte = cipher.doFinal(bytesrc);
        return new String(retByte, "UTF-8");
    }




    /**
     * 解密，方式为先解码base64，再解密，转成正常字符串
     *
     * @param base64message  编码的密文
     * @param key     密钥
     * @return        解密后的文本
     * @throws Exception
     */
    public static String decrypt_base64(String base64message, String key) throws Exception {
        String message = new String(Base64.decodeBase64(base64message.getBytes("UTF-8")));
        String result = decrypt(message, key);
        return result;
    }
    /**
     * 转成16进制
     *
     * @param b
     * @return
     */
    public  static String toHexString(byte b[]) {
        StringBuffer hexString = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            String plainText = Integer.toHexString(0xff & b[i]);
            if (plainText.length() < 2)
                plainText = "0" + plainText;
            hexString.append(plainText);
        }

        return hexString.toString();
    }

    public  static byte[] convertHexString(String ss) {
        byte digest[] = new byte[ss.length() / 2];
        for (int i = 0; i < digest.length; i++) {
            String byteString = ss.substring(2 * i, 2 * i + 2);
            int byteValue = Integer.parseInt(byteString, 16);
            digest[i] = (byte) byteValue;
        }

        return digest;
    }

}
